/**
 * \file kernel/syscall.c
 * \author Davide Gessa
 * \date 10-12-09
 */
#include <syscall.h>
#include <types.h>
#include <arch.h>
#include <debug.h>
#include <util.h>
#include <task/task.h>
#include <task/term.h>
#include <task/service.h>
#include <driver.h>


/**
 * Effetua una chiamata ad una call
 * 
 * \param call numero della syscall
 * \param ebx parametro 1
 * \param ecx parametro 2
 * \param edx parametro 3
 */
int syscall_call(uint32_t call, uint32_t ebx, uint32_t ecx, uint32_t edx)
{
	regs_t reg;
	reg.eax = call;
	reg.ebx = ebx; 
	reg.ecx = ecx; 
	reg.edx = edx; 
	asm("movl %0, %%eax\n\t": : "g" (&reg));
	
	asm ("int %0\n" : : "g" (SYSCALL_INT));
	
	asm ("movl %%eax, %0\n\t" : "=r" (reg.eax));
	return reg.eax;
}


/**
 * Handler delle chiamate alla syscall
 * 
 * \param r tutti i registri salvati al momento dell'interruzione
 */
void syscall_callback(regs_t *r)
{	
	regs_t *reg;
	int ret;
	
	// Preleva gli argomenti
	asm ("movl %%eax, %0\n\t" : "=r" (reg));
		 
		 
	switch(reg->eax)
	{
		/*
		 * Call di sistema
		 */
		case SYS_INFO:
			sysinfo_get((sysinfo_t *) reg->ebx);
			break;

		/*
		 * IO su porte
		 */
		case IO_INB:
			ret = io_inb((uint16_t) reg->ebx);
			break;
			
		case IO_OUTB:
			io_outb((uint16_t) reg->ebx, (uint8_t) reg->ecx);
			break;
			
		case IO_INW:
			ret = io_inw((uint16_t) reg->ebx);
			break;
			
		case IO_OUTW:
			io_outw((uint16_t) reg->ebx, (uint16_t) reg->ecx);
			break;
			
			
		/*
		 * Hardware
		 */
		case HW_HANDLER_REG:
			handler_reg((uint8_t) reg->ebx, (void (*)(regs_t *)) reg->ecx);			
			break;
			
		case HW_HANDLER_UNREG:
			handler_unreg((uint8_t) reg->ebx);
			break;
			
		case HW_DEBUGOUT:
			video_puts((char *) reg->ebx);
			break;
			
		case HW_SHUTDOWN:
			shutdown();
			break;
		
			
		/*
		 * Task
		 */
		case TASK_GET_PID:
			ret = task_get_pid();
			break;
			
		case TASK_GET_TERM:
			ret = (uint32_t) task_get_from_pid(task_get_pid())->term;
			
		case MSG_SEND:
			ret = message_send((pid_t) reg->ebx, (char *) reg->ecx, (uint32_t) reg->edx);
			break;
			
		case MSG_RECV:
			ret = message_recv((char *) reg->ebx, (pid_t *) reg->ecx);
			break;
			
		/*
		 * Call di server
		 */
		 case SRV_CREATE:
		 	ret = service_create((char *) reg->ebx);
		 	break;
		 	
		 case SRV_DESTROY:
		 	ret = service_destroy((char *) reg->ebx);
		 	break;
		 	
		 case SRV_START:
		 	ret = service_start((char *) reg->ebx);
		 	break;
		 	
		 case SRV_STOP:
		 	ret = service_stop((char *) reg->ebx);
		 	break;
		 	
		 case SRV_GET_X:
		 	ret = (uint32_t) service_get_x((int) reg->ebx);
		 	break;
		 	
		 case SRV_GET:
		 	ret = (uint32_t) service_get((char *) reg->ebx)->pid;
		 	break;
			
			
		/*
		 * Call di driver
		 */
		case DRV_ADD:
			ret = (uint32_t) driver_add((driver_t *) reg->ebx);
			break;
			
		case DRV_DEL:
			ret = (uint32_t) driver_del((char *) reg->ebx);
			break;
			
		case DRV_GET_X:
			ret = (uint32_t) driver_get_x((uint32_t) reg->ebx);
			break;
			
		case DRV_GET:
			ret = (uint32_t) driver_get((char *) reg->ebx);
			break;

		
		/*
		 * Memory manager
		 */
		case MM_FREE:
			mm_kfree((void *) reg->ebx);
			break;
			
		case MM_ALLOC:
			ret = mm_kalloc((size_t) reg->ebx);
			break;
			
		case MM_GETUSED:
			ret = mm_get_used_mem();
			break;
			
		case MM_GETTOTAL:
			ret = mm_get_total_mem();
			break;
			
			
		/*
		 * Non valido
		 */
		default:
			video_puts("not valid call ");
			video_putint(reg->eax);
			video_puts("\n");
			break;
	}
	
	
	// Restituisce il valore di ritorno
	asm("movl %0, %%eax\n\t": : "g" (ret));
	

}



/**s
 * Inizializza le syscall
 */
void syscall_init()
{
	DEBUGP("syscall_init()\n");
}


